# -*- coding: utf-8 -*-

from cave import Cave

class NatureException(Exception):
    pass

class Nature:
    """Rules of nature, simulates environment, keeps track where agent is, what are reactions to agent actions."""
    c = None        # cave object
    a = None        # agent position
    d = (           # directions
        Cave.N, Cave.E, Cave.S, Cave.W)   
    o = 1           # orientation, index in d (ex. 1=E)
    g = False       # gold found?
    r = True        # has arrow?
    n = 0           # number of moves
    p = None        # last percepts for debuging
    
    Actions  = set('Forward Left Right Grab Shoot Climb'.split(' '))
    Percepts = set('Stench Bats Breeze Glitter Bump Scream Light'.split(' '))

    def __init__(self, cave):
        self.c = cave
        self.g = False
    def __str__(self):
        """Some debug info"""
        return '\n'.join([
            "CAVE: %s" % self.c,
            "KNIGHT:",
            "\tis at: %s" % str(self.a),
            "\theads: %s" % ['N','E','S','W'][self.o],
            "\tlast percept: %s" % (', '.join(self.p) if self.p else 'None'), 
            "\thas %sgold" % ('' if self.g else 'NO '),
            "\thas %sarrow" % ('' if self.r else 'NO '),
        ])
        
    def perceptAt(self, r=None):
        """Returns positional percepts (Stench Bats Breeze Glitter Light)"""
        if not r: r=self.a
        pct = set()
        if 'G' in self.c[r]: pct.add('Glitter')
        if 'E' in self.c[r]: pct.add('Light')        

        nbh = set([r,Cave.N(r),Cave.S(r),Cave.W(r),Cave.E(r)])
        if self.c.roomsWith('B') & nbh: pct.add('Bats')
        if self.c.roomsWith('P') & nbh: pct.add('Breeze')
        if self.c.roomsWith('W') & nbh: pct.add('Stench')
 
        return pct
    
    def init(self):
        """Places agent at the entrance, returns percepts"""
        try:
            self.a = self.c.roomsWith('E').pop()
            self.o = 1
            self.p = self.perceptAt()
            return self.p
        except IndexError as E:
            print 'Fatal error: no entrance'

    def fly(self, r):
        bf = self.c.bats & self.c[r]
        return set(self.c.rooms(withsome=bf)).remove(r).pop()
     
    def react(self, action):
        """Executes action, returns percepts"""
        self.n += 1
        pct = set()
        if action == 'Forward':
            rf = self.d[self.o](self.a)     # room forward
            if 'S' in self.c[rf]:
                pct.add('Bump')
            elif 'W' in self.c[rf] or 'P' in self.c[rf]:
                raise NatureException('Death', self.n)  
            elif 'B' in self.c[rf]:
                self.a = self.fly(rf)
            else:
                self.a = rf            
        elif action == 'Left':
            self.o = (self.o-1) % 4
        elif action == 'Right':
            self.o = (self.o+1) % 4
        elif action == 'Grab':
            if 'G' in self.c[self.a]:
                self.g = True
                self.c[self.a].remove('G')
        elif action == 'Shoot' and self.r:
            self.r = False          #use arrow
            r = self.a
            i = set(['W','S'])
            while not i & self.c[r]:
                r = self.d[self.o](r)
            if 'W' in self.c[r]:
                pct.add('Scream')
                self.c[r].remove('W')
        elif action == 'Climb':
            if 'E' in self.c[self.a]:
                raise NatureException('Win' if self.g else 'Escape', self.n)
            elif 'B' in self.c[self.a]:
                self.a = self.fly(self.a)
            else:
                pct.add('Bump')
        self.p = pct | self.perceptAt()
        return self.p

if __name__ == "__main__":
    c = Cave('|EG| |')
    n = Nature(c)
    n.init()    
    print n
    print
    try:
        for m in ['Forward', 'Left', 'Forward', 'Left', 'Forward', 'Left', 'Forward', 'Forward', 'Climb', 'Right', 'Right', 'Forward', 'Grab', 'Climb']:
            n.react(m)
            print 'ACTION: %s' % m 
            print n    
            print
    except Exception as E:
        print E
    


